package com.kun.companion.core.http;


import com.kun.companion.core.context.annotation.Component;
import com.kun.companion.core.context.annotation.PostConstruct;
import okhttp3.*;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;

/**
 * OkHttp服务封装类，提供HTTP客户端实例管理及插件扩展能力
 *
 * @author gzc
 */
@Component
public class OkHttpService {
    private static final Logger log = LoggerFactory.getLogger(OkHttpService.class);

    private final OkHttpClient instance;
    private final ClientConfig config;
    /**
     * 线程安全的插件列表
     */
    private final List<HttpPlugin> pluginList = new CopyOnWriteArrayList<>();
    private final Map<Object, Call> activeCallMap = new ConcurrentHashMap<>();

    /**
     * 构造方法（自定义配置）
     *
     * @param config 客户端配置，不可为null
     */
    public OkHttpService(ClientConfig config) {
        this.config = Objects.requireNonNull(config);
        this.instance = buildClient(config);
    }


    public OkHttpService() {
        this(ClientConfig.defaultConfig());
    }

    /**
     * 注册全局插件（线程安全）
     *
     * @param plugin 待注册插件实例
     */
    public void registerPlugin(HttpPlugin plugin) {
        pluginList.add(Objects.requireNonNull(plugin));
    }

    /**
     * 全局拦截器实现（插件功能入口）
     */
    private class GlobalInterceptor implements Interceptor {
        @NotNull
        @Override
        public Response intercept(@NotNull Chain chain) throws IOException {
            Request original = applyPrePlugins(chain.request());
            try {
                Response response = chain.proceed(original);
                return applyPostPlugins(response);
            } catch (Exception e) {
                handleError(original, e);
                throw e;
            }
        }

        /**
         * 依次执行请求预处理插件
         */
        private Request applyPrePlugins(Request request) {
            for (HttpPlugin plugin : pluginList) {
                request = plugin.preProcess(request);
            }
            return request;
        }

        /**
         * 依次执行响应后处理插件
         */
        private Response applyPostPlugins(Response response) {
            for (HttpPlugin plugin : pluginList) {
                response = plugin.postProcess(response);
            }
            return response;
        }

        /**
         * 触发所有插件的错误处理
         */
        private void handleError(Request request, Exception e) {
            pluginList.forEach(plugin -> plugin.onError(request, e));
        }
    }

    /**
     * 构建OkHttpClient实例
     *
     * @param config 客户端配置
     * @return 配置完成的实例
     */
    private OkHttpClient buildClient(ClientConfig config) {
        OkHttpClient.Builder builder = new OkHttpClient.Builder()
                .connectTimeout(config.getConnectTimeout(), TimeUnit.SECONDS)
                .readTimeout(config.getReadTimeout(), TimeUnit.SECONDS)
                .writeTimeout(config.getWriteTimeout(), TimeUnit.SECONDS)
                .addInterceptor(new GlobalInterceptor());

        // 添加用户自定义拦截器（非全局插件）
        config.getInterceptorList().forEach(builder::addInterceptor);

        // 配置连接池参数，连接池保活时间单位需为分钟
        ConnectionPool pool = new ConnectionPool(
                config.getPoolConfig().getMaxIdle(),
                config.getPoolConfig().getKeepAlive(),
                TimeUnit.MINUTES);
        builder.connectionPool(pool)
                .pingInterval(config.getPoolConfig().getPingInterval(), TimeUnit.SECONDS);

        return builder.build();
    }

    /**
     * 执行请求并返回响应字符串
     *
     * @param request 构建好的请求对象
     * @throws IOException 网络异常或响应码非200
     */
    public String execute(Request request) throws IOException {
        try (Response response = instance.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                throw new IOException("Unexpected code: " + response);
            }
            return Objects.requireNonNull(response.body()).string();
        }
    }

    /**
     * 执行同步GET请求
     *
     * @param url 请求地址
     * @return 响应体字符串
     * @throws IOException 网络异常或中断
     */
    public String get(String url) throws IOException {
        Request request = new Request.Builder()
                .url(url)
                .get()
                .build();
        return execute(request);
    }

    /**
     * 执行带参数的同步GET请求
     *
     * @param url    基础地址
     * @param params 查询参数
     */
    public String get(String url, Map<String, String> params) throws IOException {
        HttpUrl.Builder urlBuilder = Objects.requireNonNull(HttpUrl.parse(url)).newBuilder();
        params.forEach(urlBuilder::addQueryParameter);
        return get(urlBuilder.build().toString());
    }

    /**
     * 增强型GET请求（自动参数编码）
     *
     * @param url     请求地址
     * @param params  查询参数Map
     * @param headers 请求头Map
     */
    public String get(String url, Map<String, String> params, Map<String, String> headers) throws IOException {
        HttpUrl httpUrl = buildUrlWithParams(url, params);
        Request request = buildRequest(httpUrl.toString(), "GET", null, headers);
        return execute(request);
    }

    /**
     * 执行同步POST请求（JSON格式）
     * @param url 请求地址
     * @param json JSON字符串
     */
    public String postJson(String url, String json) throws IOException {
        MediaType JSON = MediaType.get("application/json; charset=utf-8");
        RequestBody body = RequestBody.create(json, JSON);
        return post(url, body);
    }

    /**
     * 通用POST请求执行方法
     * @param url 请求地址
     * @param body 请求体
     */
    public String post(String url, RequestBody body) throws IOException {
        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();
        return execute(request);
    }

    /**
     * 执行同步POST请求（表单格式）
     * @param url 请求地址
     * @param formParams 表单参数
     */
    public String postForm(String url, Map<String, String> formParams) throws IOException {
        FormBody.Builder builder = new FormBody.Builder();
        formParams.forEach(builder::add);
        return post(url, builder.build());
    }

    /**
     * 增强型POST请求（支持多种媒体类型）
     *
     * @param url       请求地址
     * @param body      请求体对象（支持String/byte[]/File）
     * @param mediaType 媒体类型
     * @param headers   请求头
     */
    public String post(String url, Object body, MediaType mediaType,
                       Map<String, String> headers) throws IOException {
        RequestBody requestBody = buildRequestBody(body, mediaType);
        Request request = buildRequest(url, "POST", requestBody, headers);
        return execute(request);
    }


    /**
     * 文件上传（多部分表单）
     * @param url 目标地址
     * @param fileParamName 文件参数名
     * @param file 上传文件
     * @param formParams 其他表单参数
     */
    public String upload(String url, String fileParamName, File file,
                         Map<String, String> formParams) throws IOException {
        MultipartBody.Builder builder = new MultipartBody.Builder()
                .setType(MultipartBody.FORM);

        // 添加文件
        MediaType mediaType = MediaType.parse("application/octet-stream");
        builder.addFormDataPart(
                fileParamName,
                file.getName(),
                RequestBody.create(file, mediaType)
        );

        // 添加其他参数
        formParams.forEach(builder::addFormDataPart);

        return post(url, builder.build());
    }

    /**
     * 流式下载大文件
     *
     * @param url          文件地址
     * @param outputStream 输出流
     */
    public void downloadLargeFile(String url, OutputStream outputStream) throws IOException {
        Request request = new Request.Builder().url(url).build();
        try (Response response = instance.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                throw new IOException("下载失败，状态码：" + response.code());
            }

            try (InputStream is = Objects.requireNonNull(response.body()).byteStream()) {
                byte[] buffer = new byte[8192];
                int bytesRead;
                while ((bytesRead = is.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead);
                }
            }
        }
    }

    /**
     * 增强型异步请求（带标签管理）
     *
     * @param request  请求对象
     * @param tag      请求标签（用于后续取消）
     * @param callback 增强回调接口
     */
    public void enqueue(Request request, Object tag, EnhancedCallback callback) {
        request = request.newBuilder().tag(tag).build();
        Call call = instance.newCall(request);

        activeCallMap.put(tag, call);
        call.enqueue(new Callback() {
            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response) {
                try {
                    Response processed = applyPostProcessing(response);
                    callback.onSuccess(call, processed);
                } catch (Exception e) {
                    callback.onFailure(call, e);
                } finally {
                    activeCallMap.remove(tag);
                }
            }

            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {
                handleRequestFailure(call.request(), e);
                callback.onFailure(call, e);
                activeCallMap.remove(tag);
            }
        });
    }

    /**
     * 按标签取消请求
     *
     * @param tag 请求时设置的标签
     */
    public void cancelRequest(Object tag) {
        Call call = activeCallMap.get(tag);
        if (call != null && !call.isCanceled()) {
            call.cancel();
            activeCallMap.remove(tag);
            log.debug("已取消标签为 {} 的请求", tag);
        }
    }


    /**
     * 以下是内部工具方法
     **/
    private HttpUrl buildUrlWithParams(String url, Map<String, String> params) {
        HttpUrl.Builder builder = Objects.requireNonNull(HttpUrl.parse(url)).newBuilder();
        params.forEach(builder::addQueryParameter);
        return builder.build();
    }

    private Request buildRequest(String url, String method, RequestBody body,
                                 Map<String, String> headers) {
        Request.Builder builder = new Request.Builder().url(url).method(method, body);
        headers.forEach(builder::addHeader);
        return builder.build();
    }

    private RequestBody buildRequestBody(Object body, MediaType mediaType) {
        if (body instanceof String) {
            return RequestBody.create((String) body, mediaType);
        } else if (body instanceof byte[]) {
            return RequestBody.create((byte[]) body, mediaType);
        } else if (body instanceof File) {
            return RequestBody.create((File) body, mediaType);
        }
        throw new IllegalArgumentException("不支持的请求体类型: " + body.getClass());
    }

    private Response applyPostProcessing(Response response) {
        // 应用插件处理
        Response processed = response;
        for (HttpPlugin plugin : pluginList) {
            processed = plugin.postProcess(processed);
        }
        return processed;
    }

    private void handleRequestFailure(Request request, Exception e) {
        pluginList.forEach(plugin -> plugin.onError(request, e));
        log.error("请求失败: {} {}", request.method(), request.url(), e);
    }

    /**
     * 增强回调接口（添加成功回调）
     */
    public interface EnhancedCallback {
        void onSuccess(Call call, Response response);

        void onFailure(Call call, Exception e);
    }
}